﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using InvoiceSample;
using CodeProject.Data.Entity;
using System.Data.Objects;
using TestProjectCS.Attributes;

namespace TestProjectCS
{
	[TestClass]
	public class SaveProductTests : BaseInvoiceTests
	{
		/// <summary>
		/// Tests the change of supplier on a product to be saved.
		/// </summary>
		[TestMethod]
		[RollingBackTransaction]
		public void ChangeSupplierTest()
		{
			// Retrieve product:
			Product prod = this.GetDetachedProductWithSupplier(10);

			Assert.AreEqual("Acme Factory Manchester", prod.Supplier.Name);

			// Change supplier:
			prod.Supplier = this.Suppliers[3];

			// Save product with supplier:
			prod = this.SaveProductWithSupplier(prod);

			// Retrieve product from store and check supplier:
			Assert.AreEqual("ACME Dublin", this.GetDetachedProductWithSupplier(10).Supplier.Name);
		}

		/// <summary>
		/// Tests the removal of supplier on a product to be saved.
		/// </summary>
		[TestMethod]
		[RollingBackTransaction]
		public void RemoveSupplierTest()
		{
			// Retrieve product:
			Product prod = this.GetDetachedProductWithSupplier(10);

			Assert.AreEqual("Acme Factory Manchester", prod.Supplier.Name);

			// Remove supplier:
			prod.Supplier = null;

			// Save product with supplier:
			prod = this.SaveProductWithSupplier(prod);

			// Retrieve product from store and check supplier is not set:
			Assert.IsNull(this.GetDetachedProductWithSupplier(10).Supplier);
		}

		/// <summary>
		/// Tests the change of supplier on a product without updating supplier properties.
		/// </summary>
		[TestMethod]
		[RollingBackTransaction]
		public void ChangeSupplierNoUpdateTest()
		{
			// Retrieve product:
			Product prod = this.GetDetachedProductWithSupplier(10);

			Assert.AreEqual("Acme Factory Manchester", prod.Supplier.Name);

			// Change supplier:
			prod.Supplier = this.Suppliers[2];

			// Change supplier properties:
			string supplierAddressBefore = prod.Supplier.Address;
			prod.Supplier.Address = "Moon";

			// Save product with supplier:
			prod = this.SaveProductWithSupplier(prod);

			// Retrieve product from store and check supplier:
			Assert.AreEqual("Acme Factory Brighton", this.GetDetachedProductWithSupplier(10).Supplier.Name);
			Assert.AreEqual(supplierAddressBefore, this.GetDetachedProductWithSupplier(10).Supplier.Address);
		}

		[TestMethod]
		[RollingBackTransaction]
		public void NewProductTest()
		{
			// Create a new product:
			Product product = new Product();
			product.Label = "New Product 101";
			product.SalePrice = 101m;
			product.Supplier = this.Suppliers[2];

			// Save it:
			Product savedProduct = this.SaveProductWithSupplier(product);

			// Verify it has a correct identifier:
			Console.WriteLine("Saved products id: {0}", savedProduct.ID);
			Assert.IsTrue(savedProduct.ID != 0);
			Assert.AreEqual("Acme Factory Brighton", savedProduct.Supplier.Name);
		}

		[TestMethod]
		[RollingBackTransaction]
		public void ManyNewProductsTest()
		{
			// Create 4 new products:
			Product[] products = new Product[4];
			products[0] = new Product();
			products[0].Label = "New Product 101";
			products[0].SalePrice = 101m;
			products[0].Supplier = this.Suppliers[2];
			products[1] = new Product();
			products[1].Label = "New Product 102";
			products[1].SalePrice = 102m;
			products[1].Supplier = null;
			products[2] = new Product();
			products[2].Label = "New Product 103";
			products[2].SalePrice = 103m;
			products[2].Supplier = this.Suppliers[3];
			products[3] = new Product();
			products[3].Label = "New Product 104";
			products[3].SalePrice = 104m;
			products[3].Supplier = this.Suppliers[3];

			// Save products:
			Product[] savedProducts = this.SaveProductsWithSupplier(products);

			// Verify products:
			Assert.AreEqual(products.Length, savedProducts.Length);
			foreach (Product prod in savedProducts)
			{
				Console.WriteLine("- {0}: '{1}', ${2} ({3})", prod.ID, prod.Label, prod.SalePrice, (prod.Supplier==null) ? "<no supplier>" : prod.Supplier.Name);
				Assert.AreNotEqual((long)0, prod.ID);
			}
		}

		[TestMethod]
		[RollingBackTransaction]
		public void UpdateProductsTest()
		{
			Product[] products = new Product[4];
			
			// Get 3 exising products:
			products[0] = this.GetDetachedProductWithSupplier(3);
			products[1] = this.GetDetachedProductWithSupplier(5);
			products[2] = this.GetDetachedProductWithSupplier(7);

			// Add an additional product:
			products[3] = new Product();
			products[3].Label = "New Product 104";
			products[3].SalePrice = 104m;
			products[3].Supplier = this.Suppliers[3];

			// Update all products:
			foreach (Product item in products)
				item.SalePrice *= 2;

			// Save changes:
			products = this.SaveProductsWithSupplier(products);

			// Verify results:
			Assert.AreEqual(2 * 12.9m, products[0].SalePrice);
			Assert.AreEqual(2 * 27.5m, products[1].SalePrice);
			Assert.AreEqual(2 * 250.95m, products[2].SalePrice);
			Assert.AreEqual(2 * 104m, products[3].SalePrice);
			Assert.AreEqual(3, products[3].Supplier.ID);
		}

		#region SaveProduct(WithSupplier) method

		/// <summary>
		/// Save product with supplier implementation using AttachObjectGraph.
		/// </summary>
		/// <param name="product">The (detached) product to be attached and saved.</param>
		/// <returns>The attached version of the product.</returns>
		Product SaveProductWithSupplier(Product product)
		{
			using (var context = new SampleInvoiceContext())
			{
				Product attachedProduct = context.AttachObjectGraph(
					product,
					p => p.Supplier.WithoutUpdate()
				);

				context.SaveChanges();

				return attachedProduct;
			}
		}

		/// <summary>
		/// Save multiple products with supplier implementation using AttachObjectGraph.
		/// </summary>
		/// <param name="product">The (detached) products to be attached and saved.</param>
		/// <returns>The attached version of the products.</returns>
		Product[] SaveProductsWithSupplier(IEnumerable<Product> products)
		{
			using (var context = new SampleInvoiceContext())
			{
				Product[] attachedProducts = context.AttachObjectGraphs(
					products,
					p => p.Supplier.WithoutUpdate()
				);

				context.SaveChanges();

				return attachedProducts;
			}
		}

		#endregion
	}
}
